package web

import (
	regexp "github.com/dlclark/regexp2"
	"github.com/gin-contrib/sessions"
	"github.com/gin-gonic/gin"
	"hh/practice/internal/domain"
	"hh/practice/internal/service"
	"net/http"
	"strings"
	"time"
	"unicode/utf8"
)

const (
	emailRegexpPattern    = `^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$`
	passwordRegexpPattern = `^(?=.*[A-Za-z])(?=.*\d)(?=.*[$@$!%*#?&])[A-Za-z\d$@$!%*#?&]{8,}$`
	birthRegexpPattern    = `^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$`
)

type UserHandler struct {
	emailRegexp    *regexp.Regexp
	passwordRegexp *regexp.Regexp
	birthRegexp    *regexp.Regexp
	svc            *service.UserService
}

func NewUserHandler(svc *service.UserService) *UserHandler {
	return &UserHandler{
		svc:            svc,
		emailRegexp:    regexp.MustCompile(emailRegexpPattern, regexp.None),
		passwordRegexp: regexp.MustCompile(passwordRegexpPattern, regexp.None),
		birthRegexp:    regexp.MustCompile(birthRegexpPattern, regexp.None),
	}
}

func (u UserHandler) RegisterRoutes(r *gin.Engine) {
	rg := r.Group("/users")
	rg.GET("/profile", u.Profile)
	rg.POST("/login", u.Login)
	rg.POST("/signup", u.SignUp)
	rg.POST("/edit", u.Edit)
}

func (u UserHandler) SignUp(c *gin.Context) {
	type SignUpReq struct {
		Email           string `json:"email"`
		Password        string `json:"password"`
		ConfirmPassword string `json:"confirmPassword"`
	}

	var req SignUpReq

	if err := c.Bind(&req); err != nil {
		return
	}

	isEmail, err := u.emailRegexp.MatchString(req.Email)
	if err != nil {
		c.String(http.StatusOK, "系统错误！")
		return
	}
	if !isEmail {
		c.String(http.StatusOK, "邮箱格式错误！")
		return
	}

	if req.Password != req.ConfirmPassword {
		c.String(http.StatusOK, "两次密码输入不一致！")
		return
	}

	isPassword, err := u.passwordRegexp.MatchString(req.Password)
	if err != nil {
		c.String(http.StatusOK, "系统错误！")
		return
	}
	if !isPassword {
		c.String(http.StatusOK, "密码不合规，至少8个字符，至少1个字母，1个数字和1个特殊字符")
		return
	}

	err = u.svc.Signup(c, domain.User{
		Email:    req.Email,
		Password: req.Password,
	})

	if err == service.ErrUserDuplicateEmail {
		c.String(http.StatusOK, "邮箱冲突！")
		return
	}

	if err != nil {
		c.String(http.StatusOK, "系统错误！")
		return
	}

	c.String(http.StatusOK, "signup")
}

func (u UserHandler) Login(c *gin.Context) {
	type Req struct {
		Email    string `json:"email"`
		Password string `json:"password"`
	}
	var req Req
	if err := c.Bind(&req); err != nil {
		return
	}
	user, err := u.svc.Login(c, req.Email, req.Password)
	switch err {
	case nil:
		sess := sessions.Default(c)
		sess.Set("userId", user.Id)
		sess.Options(sessions.Options{
			MaxAge: 900,
		})
		err := sess.Save()
		if err != nil {
			c.String(http.StatusOK, "系统错误！")
			return
		}
		c.String(http.StatusOK, "登录成功！")
	case service.ErrUserNotFound:
		c.String(http.StatusOK, "邮箱还没有注册！")
	case service.ErrPasswordWrong:
		c.String(http.StatusOK, "密码输入错误！")
	default:
		c.String(http.StatusOK, "系统错误！")
	}
}

func (u UserHandler) Edit(c *gin.Context) {
	type Req struct {
		Name  string `json:"name"`
		Birth string `json:"birth"`
		Intro string `json:"intro"`
	}
	var req Req
	if err := c.Bind(&req); err != nil {
		return
	}

	req.Name = strings.TrimSpace(req.Name)
	nameLen := utf8.RuneCountInString(req.Name)
	isName := nameLen >= 3 && nameLen <= 16
	if !isName {
		c.String(http.StatusOK, "昵称需3-16个字！")
		return
	}
	_, err := time.Parse(time.DateOnly, req.Birth)
	if err != nil {
		c.String(http.StatusOK, "日期非法或格式错误，生日格式必须为yyyy-mm-dd，如1992-01-01")
		return
	}

	isIntro := utf8.RuneCountInString(req.Intro) <= 150
	if !isIntro {
		c.String(http.StatusOK, "简介不能超过150个字")
		return
	}

	sess := sessions.Default(c)
	uid := sess.Get("userId")
	if uid == nil {
		c.String(http.StatusOK, "系统错误")
		return
	}

	err = u.svc.Edit(c, domain.UserProfile{
		Name:  req.Name,
		Birth: req.Birth,
		Intro: req.Intro,
		Uid:   uid.(int64),
	})

	switch err {
	case nil:
		c.String(http.StatusOK, "修改成功")
	case service.ErrUserNameDuplicate:
		c.String(http.StatusOK, "用户名已被使用")
	default:
		c.String(http.StatusOK, "系统错误")
	}
}

func (u UserHandler) Profile(c *gin.Context) {
	sess := sessions.Default(c)
	uid := sess.Get("userId")
	if uid == nil {
		c.String(http.StatusOK, "系统错误")
		return
	}

	profile, err := u.svc.Profile(c, uid.(int64))
	if err == service.ErrUserProfileNotFound {
		c.String(http.StatusOK, "没有用户信息")
		return
	}
	if err != nil {
		c.String(http.StatusOK, "系统错误")
		return
	}
	c.JSON(http.StatusOK, profile)
}
